package 多线程2;

public class TestDemo5 {
//当前代码是否为线程安全的呢
    //懒汉模式
    static class Singleton{
        private Singleton(){

        }
        private static volatile Singleton instance = null;

//        public static Singleton getInstance() {
//            if(instance == null){
//                instance = new Singleton();
//            }
//            return instance;
//        }
    /**
     * 如果实例已经创建完毕,后续再调用getinstance,此时不涉及修改操作.此时仍然是线程安全的
     * 但是如果实例尚未创建，此时就可能会涉及修改
     * 如果确实存在多个线程同时修改,就会涉及到线程安全问题
     * --
     */
    //这样可行不?--
//    public static Singleton getInstance() {
//        if(instance == null){
//            synchronized (Singleton.class){
//                instance = new Singleton();
//            }
//        }
//        return instance;
//    }
    /**
     * 注意!!这是一种典型的错误写法!!!
     * 此处的线程不安全,主要是因为if操作和=操作不是原子的.
     * 要想解决这里的线程安全问题,就需要把这两个操作变成原子.
     * 需要使用synchronized给他们包裹上
     */
//    public static Singleton getInstance() {
//
//        synchronized (Singleton.class){
//            if(instance == null){
//                instance = new Singleton();
//            }
//        }
//        return instance;
//    }
    /**
     *  这才是科学的写法
     *  但是,如果这样加锁,确实线程安全问题,解决了.但是引入了新问题.
     *  getinstance是首次调用的时候才会涉及线程安全问题.后续,也会涉及到加锁操作
     *
     *  按照刚才这个加锁的写法,不仅仅是首次调用,包括后续的调用,也会涉及到加锁操作
     *  后续本来没有线程安全问题,不需要加锁.再尝试加锁,就是多次一举了(这里是有问题)
     *  加锁操作本身是一个开销比较大的操作,此处不该加锁的地方加锁了,可能会让这个代码的速度降低很多倍
     *  (之前是为了代码稳定准确,慢点就慢点,但这后续本来就是准确的,还去加锁,难受的)
     */
    //改进思路:首次调用的时候,就加锁,后续调用的时候,不加锁
    public static Singleton getInstance() {
        if (instance == null){//区分首批调用还是后续调用,决定要不要加锁
            synchronized (Singleton.class){
                if(instance == null){//第一批调用的这些线程,其中谁是那个能获取到锁的幸运儿
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
    /**
     * 改进后的代码,我们发现,同一个条件,在synchronized内外各自出现了一次
     * 如果没有synchronized这样的代码是无意义的.
     * 但是有了synchronized之后,这个代码就很关键了
     */

    /**
     * 刚才的代码中,还涉及到一个很重要的问题!!!
     * 当首批线程通过第一层if,进入到锁阶段,并创建好对象后,这个时候,相当于已经把内存中instance的值,修改成非null了
     * 后续批次的线程,通过第一层if的时候,也需要判定instance的值,但是这个判定不一定是从内存读的数据,也可能是从寄存器读的数据
     *
     */

}

    public static void main(String[] args) {
        //通过这个方法来获取到实例,就能保证只有唯一实例
        Singleton instance = Singleton.getInstance();
    }
}
